package test.AllJoyn.ServerClient;

import org.alljoyn.bus.BusAttachment;
import org.alljoyn.bus.BusException;
import org.alljoyn.bus.BusObject;
import org.alljoyn.bus.Mutable;
import org.alljoyn.bus.SessionOpts;
import org.alljoyn.bus.SessionPortListener;
import org.alljoyn.bus.SignalEmitter;
import org.alljoyn.bus.Status;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class testAllJoynSC extends Activity {
	
	static{
		System.loadLibrary("alljoyn_java");
	}

	private EditText mEditText;
	private ArrayAdapter<String> mArrayAdapter;
	private ListView mListView;
	
	//for the preference
	private Menu menu;
	
	//inherits from the alljoyn interface, containing the bus method and signal
	private testService mService;
	
	//the alljoyn interface, used to communicate with client side. The client must use the same interface
	private testInterface myInterface;
	
	//handle the message inside of server
	private Handler mBusHandler;
	
	//the client
	private Client mClient;
	
	private Listener mListener = new Listener();
	
	public static final int PING = 0;
	public static final int REPLY = 1;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
        
        mListView = (ListView) findViewById(R.id.listView1);
        mListView.setAdapter(mArrayAdapter);
        
        mEditText = (EditText) findViewById(R.id.editText1);
        mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
			
			@Override
			public boolean onEditorAction(TextView arg0, int arg1, KeyEvent arg2) {
				if ((EditorInfo.IME_NULL == arg1) && (arg2.getAction() == KeyEvent.ACTION_DOWN))
				{
					//show the message on the screen
					Message msg = mHandler.obtainMessage(PING, arg0.getText().toString());
					mHandler.sendMessage(msg);
					
					//call the remote method for the version 1
					//the implement part is in the service part and client uses RMI to call this method belonged to the
					//bus interface
//					mClient.sendMessage((Object)arg0.getText().toString());
					
					/*call the signal for the version 2
					  the implement part is in the client part
					  when server send the signal through puttin data into the parameter of BusSignal
					  the data would be send to the client
					*/
					try {
						myInterface.communicateText(arg0.getText().toString());
					} catch (BusException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				return true;
			}
		});
        
        //create an instance of client
        mClient = new Client(mListener);
        
        //this class inherits from an alljoyn interface, marked with BusInterface, 
        //containing bus signal and method
        //signal is used in version 2
        //method is used in version 1
        mService = new testService();
        
        HandlerThread busThread = new HandlerThread("ServiceBusHandler");
        busThread.start();
        mBusHandler = new ServiceBusHandler(busThread.getLooper());
        
        mBusHandler.sendEmptyMessage(ServiceBusHandler.CONNECTED);
        
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
    	MenuInflater inflater = getMenuInflater();
    	inflater.inflate(R.menu.menu, menu);
    	this.menu = menu;
    	return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
    	switch (item.getItemId())
    	{
	    	case R.id.quit:
	    	{
	    		finish();
	    		return true;
	    	}
	    	default:
	    		return super.onOptionsItemSelected(item);
    	}
    }
    
    //the handler handle the ping of reply message
    private Handler mHandler = new Handler()
    {
    	@Override
    	public void handleMessage(Message msg)
    	{
    		switch(msg.what)
    		{
	    		case PING:
	    		{
	    			String ping = (String)msg.obj;
	    			mArrayAdapter.add("Ping: " + ping);
	    			break;
	    		}
	    		case REPLY:
	    		{
	    			String reply = (String)msg.obj;
	    			mArrayAdapter.add("Reply: " + reply);
	    			break;
	    		}
	    		default:
	    			break;
    		}
    	}
    };
    
    private void logStatus(String msg, Status status)
	{
		String log = String.format("%s : %s", msg, status);
		if (status == Status.OK)
		{
			Log.i(sharedName.TAG, log);
		}
		else
		{
			Toast.makeText(getApplicationContext(), log, Toast.LENGTH_LONG).show();
			Log.e(sharedName.TAG, log);
		}
	}
    
	
    //this inherits from the bus interface, if you use the bus method, you need implement it in the server side
    //if bus signal, you need implement it in the client side
	class testService implements testInterface, BusObject
	{
		@Override
		public String Ping(String msg)
		{
			/* when the Bus Method Ping() is called by the client side,
			 * it sends message containing the REPLY label and message content,
			 * to the Screen handler*/
			mHandler.sendMessage(mHandler.obtainMessage(REPLY, msg));
			return msg;
		}

		@Override
		public void communicateText(String msg) throws BusException {
			// TODO Auto-generated method stub
		}
	}
	
	class ServiceBusHandler extends Handler
	{
		private BusAttachment mBusAttach;
		
		private static final int CONNECTED = 0;
		private static final int DISCONNECTED = 1;
		
		//when join the session, this is its id and name 
		private int sessionId;
		private String joinerName;
		
		
		public ServiceBusHandler(Looper looper)
		{
			super(looper);
		}
		
		@Override
		public void handleMessage(Message msg)
		{
			switch (msg.what)
			{
				case CONNECTED:
				{
					mBusAttach = new BusAttachment(getClass().getName(), 
							BusAttachment.RemoteMessage.Receive);
					
					Status status = mBusAttach.registerBusObject(mService, "/service");
					if (Status.OK != status)
					{
						finish();
						logStatus("Service:registerBusObject()", status);
						return;
					}
					
					status = mBusAttach.connect();
					if (Status.OK != status)
					{
						logStatus("Service:connect()", status);
						finish();
						return;
					}
					
					int flag = 0;
					status = mBusAttach.requestName(sharedName.Service_name, flag);
					if (Status.OK != status)
					{
						logStatus("Service:requestName()", status);
						finish();
						return;
					}
					
					status = mBusAttach.advertiseName(sharedName.Service_name, SessionOpts.TRANSPORT_ANY);
					if (Status.OK != status)
					{
						logStatus("Service:advertiseName()", status);
						mBusAttach.releaseName(sharedName.Service_name);
						finish();
						return;
					}
					
//					Toast.makeText(testAllJoynSC.this, "Server: success in connecting to the bus", Toast.LENGTH_LONG).show();
					
					Mutable.ShortValue contactPort = new Mutable.ShortValue(sharedName.Contact_Port);
					
					SessionOpts sessionOpts = new SessionOpts();
					sessionOpts.traffic = SessionOpts.TRAFFIC_MESSAGES;
					sessionOpts.isMultipoint = false;
					sessionOpts.proximity = SessionOpts.PROXIMITY_ANY;
					sessionOpts.transports = SessionOpts.TRANSPORT_ANY;
					
					status = mBusAttach.bindSessionPort(contactPort, sessionOpts, new SessionPortListener()
					{
						@Override
						public boolean acceptSessionJoiner(short port, String joiner, SessionOpts opts)
						{
							if (sharedName.Contact_Port == port)
							{
//								Toast.makeText(testAllJoynSC.this, "Server: accept client", Toast.LENGTH_LONG).show();
								return true;
							}
							else
							{
								return false;
							}
						}
						
						@Override
						public void sessionJoined(short sessionPort, int id, String joiner)
						{
							joinerName = joiner;
							sessionId = id;
							
							
						}
					});
					if (Status.OK != status)
					{
						logStatus("Service:bindSessionPort()", status);
						finish();
						return;
					}
					
					
					//use the signal Emitter to emit the signal
					SignalEmitter emitter = new SignalEmitter(mService, 
															  joinerName, 
															  sessionId, 
															  SignalEmitter.GlobalBroadcast.Off);
					
					myInterface = emitter.getInterface(testService.class);
					
					
					
					
					
					break;
				}
				case DISCONNECTED:
				{
					mBusAttach.unregisterBusObject(mService);
					mBusAttach.disconnect();
					getLooper().quit();
					break;
				}
				default:
					break;
			}
		}
	}
	
	class Listener
	{
		public void OnComplete(String msg)
		{
			mHandler.sendMessage(mHandler.obtainMessage(REPLY, msg));
		}
	}
}